package com.adjy.pathbuilders.touch;

import static com.adjy.pathbuilders.commons.Preconditions.check;
import static com.adjy.pathbuilders.commons.Preconditions.checkNotNull;

import java.util.List;
import java.util.Random;

import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.view.MotionEvent;

import com.adjy.pathbuilders.commons.Util;
import com.adjy.pathbuilders.objects.GameEntityType;
import com.adjy.pathbuilders.rendering.DisplayConverter;
import com.adjy.pathbuilders.rendering.GameCanvas;
import com.adjy.pathbuilders.rendering.Point;
import com.adjy.pathbuilders.rendering.Renderable;
import com.adjy.pathbuilders.rendering.loading.AnimationFactory;
import com.adjy.pathbuilders.world.GameEntity;
import com.adjy.pathbuilders.world.GameWorld;

public class ObjectBox implements Renderable {
	
	private static final float OBJECT_PRESS_RADIUS = 40;
	private static final float LINE_THICKNESS = 40;
	private static final int LINE_ALPHA = 50;
	
	private AnimationFactory anFactory;
	private TouchableObject[] objects;
	private Bitmap background;
	private GameWorld gameWorld;
	
	private int selectedObjectIndex;
	
	// Screen Positions
	private float x, y, width, height;
	
	private Paint linePaint;
	
	private List<GameEntityType> objectsToShow;
	
	public ObjectBox(int maxObjects, List<GameEntityType> objectsToShow, AnimationFactory anFactory, GameWorld gameWorld) {
		check(maxObjects >= 0, "maxObjects must not be negative.");
		checkNotNull(objectsToShow, "objectsToShow must not be null.");
		checkNotNull(anFactory, "anFactory must not be null.");
		checkNotNull(gameWorld, "gameWorld must not be null.");
		
		this.anFactory = anFactory;
		this.gameWorld = gameWorld;
		this.objectsToShow = objectsToShow;
		objects = new TouchableObject[maxObjects];
		
		mixObjects(objectsToShow);
		
		Point screenSize = DisplayConverter.getDisplaySize();
		width = screenSize.getX() / 3f;
		height = screenSize.getY() / 3f;
		x = width * 2;
		y = 0;
		selectedObjectIndex = -1;
		
		linePaint = new Paint();
		linePaint.setAntiAlias(true);
		linePaint.setColor(Color.RED);
		linePaint.setStyle(Style.FILL);
	}
	
	private void mixObjects(List<GameEntityType> objectsToShow) {
		int numberOfSwaps = objectsToShow.size();
		
		Random r = new Random();
		
		for (int i = 0; i < numberOfSwaps; i++) {
			int index1 = r.nextInt(objectsToShow.size());
			int index2 = r.nextInt(objectsToShow.size());
			
			swap(objectsToShow, index1, index2);
		}
	}

	private void swap(List<GameEntityType> objectsToShow, int index1,
			int index2) {
		GameEntityType aux = objectsToShow.get(index1);
		objectsToShow.set(index1, objectsToShow.get(index2));
		objectsToShow.set(index2, aux);
	}

	public void load() {
		anFactory.loadImages("object_box_bg");
		this.background = Bitmap.createScaledBitmap(anFactory.getImage("object_box_bg"), // FIXME: this should be static
				(int) width, (int) height, true);
		anFactory.releaseImages("object_box_bg");
		fillObjects();
	}
	
	public void release() {
		this.background.recycle();
	}
	
	private void fillObjects() {
		for (int i = 0 ; i < objects.length ; i++) {
			if (objects[i] == null) {
				addEntityAtIndex(i);
			}
		}
	}
	
	private void addEntityAtIndex(int i) {
		GameEntityType next = GameEntityType.CLOUD;
		if (objectsToShow.size() > 0) {
			next = objectsToShow.remove(0);
		} else {
			return; // Limit reached
		}
		
		float[] pos = getPositionFor(i);
		objects[i] = new TouchableObject(pos[0], pos[1], next, 
						anFactory.getAnimationsFor(next));	
	}
	
	private float[] getPositionFor(int i) {
		return new float[] {x + width/4 * (i + 1), height/2};
	}
	
	public void step(float timeElapsed) {
		for (int i = 0 ; i < objects.length ; i++) {
			if (objects[i] != null) {
				objects[i].step(timeElapsed);
			}
		}
	}

	@Override
	public void render(GameCanvas canvas, float timeElapsed) {
		if (background == null || background.isRecycled())
			return; // FIX this should never happen
		canvas.drawBitmap(background, x, y);
		
		if (selectedObjectIndex != -1) {
			// Draw Lines
			GameEntity entity = objects[selectedObjectIndex].createEntity(false);
			drawLine(objects[selectedObjectIndex].getCenter(),
					gameWorld.checkCollisionFor(entity) ? Color.RED : Color.GREEN,
							canvas);
			
		}
		
		for (int i = 0 ; i < objects.length ; i++) {
			if (objects[i] != null)
				objects[i].render(canvas, timeElapsed);
		}
		
	}

	public void captureInput(MotionEvent event) {
		switch (event.getActionMasked()) {
		case MotionEvent.ACTION_DOWN:
			touchPressed(event.getX(), event.getY());
			break;
		case MotionEvent.ACTION_MOVE:
			touchDragged(event.getX(), event.getY());
			break;
		case MotionEvent.ACTION_UP:
			touchReleased(event.getX(), event.getY());
			break;
		default:
			break;
		}
	}

	private void touchReleased(float x, float y) {
		if (selectedObjectIndex == -1)
			return;
		
		if (!(x < this.x || y > this.height)) { // Inside the Box
			moveObjectBack();
		} else {
			// Put it in GameWorld
			if (gameWorld.addTouchableObject(objects[selectedObjectIndex])) {
				objects[selectedObjectIndex] = null;
				fillObjects();
			} else {
				System.out.println("moveu de volta");
				moveObjectBack();
			}
		}
		
		selectedObjectIndex = -1;
	}

	private void moveObjectBack() {
		objects[selectedObjectIndex].resetPosition();
	}
	
	private void touchDragged(float x, float y) {
		if (selectedObjectIndex != -1)
			objects[selectedObjectIndex].setPosition(x, y);
		
	}

	private void touchPressed(float x, float y) {
		
		if (x < this.x || y > this.height) {
			// Inside the box
			return;
		}
		
		selectedObjectIndex = -1;
		for (int i = 0 ; i < objects.length ; i++) {
			if (objects[i] == null)
				continue;
			
			Point center = objects[i].getCenter();
			if (Util.getDistance(x, y, center.getX(), center.getY()) < OBJECT_PRESS_RADIUS) {
				selectedObjectIndex = i;
				break;
			}
		}
	}
	
	private void drawLine(Point pos, int color, GameCanvas canvas) {
		
		float halfThickness = LINE_THICKNESS / 2f;
		linePaint.setColor(color);
		linePaint.setAlpha(LINE_ALPHA);
		Point size = DisplayConverter.getDisplaySize();
		
		// Horizontal Line
		canvas.getCanvas().drawRect(pos.getX() - halfThickness, 0, pos.getX() + halfThickness, size.getY(), this.linePaint);
		// Vertical Line
		canvas.getCanvas().drawRect(0, pos.getY() - halfThickness, size.getX(), pos.getY() + halfThickness, this.linePaint);
	}
}
